package com.dokimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.gulimall.order.enume.OrderStatusEnum;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.dokimall.common.exception.NostockExcption;
import com.dokimall.common.to.mq.OrderTo;
import com.dokimall.common.to.mq.SeckillOrderTo;
import com.dokimall.common.utils.R;
import com.dokimall.common.vo.MemberVo;
import com.dokimall.order.Constant.OrderConstant;
import com.dokimall.order.entity.OrderItemEntity;
import com.dokimall.order.entity.PaymentInfoEntity;
import com.dokimall.order.feign.CartFeignService;
import com.dokimall.order.feign.MemberFeignService;
import com.dokimall.order.feign.ProductFeignService;
import com.dokimall.order.feign.WareFeignService;
import com.dokimall.order.interceptor.OrderInterceptor;
import com.dokimall.order.service.OrderItemService;
import com.dokimall.order.service.PaymentInfoService;
import com.dokimall.order.to.OrderCreateTo;
import com.dokimall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dokimall.common.utils.PageUtils;
import com.dokimall.common.utils.Query;

import com.dokimall.order.dao.OrderDao;
import com.dokimall.order.entity.OrderEntity;
import com.dokimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {


    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    PaymentInfoService paymentInfoService;

    private static ThreadLocal<OrderSubimtVo> orderSubimtVoThreadLocal = new ThreadLocal<>();


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberVo memberVo = OrderInterceptor.threadLocal.get();


        // 获取之前的请求
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> userAddrFuture = CompletableFuture.runAsync(() -> {
            // 远程查询用户收货地址列表
            // 每一个异步线程都共享之前的请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddrVo> userAddr = memberFeignService.getUserAddr(memberVo.getId());
            orderConfirmVo.setAddress(userAddr);
        }, executor);


        CompletableFuture<Void> checkedItemFuture = CompletableFuture.runAsync(() -> {
            // 远程查询订单选中商品列表
            // feign在远程调用之前要构造一个新请求，会丢失老请求的请求头（没了cookie用户的数据）
            // 会调用很多拦截器，我们可以定制拦截器（DokimFeignConfig）来解决问题
            // 每一个异步线程都共享之前的请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> checkedItem = cartFeignService.getCheckedItem();
            orderConfirmVo.setItems(checkedItem);
        }, executor).thenRunAsync(()->{
            // 查找商品是否有库存
            List<OrderItemVo> items = orderConfirmVo.getItems();
            List<Long> ids = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
            R r = wareFeignService.hasStock(ids);
            List<SkuHasStockVo> data = r.getData(new TypeReference<List<SkuHasStockVo>>() {});
            if(data!=null){
                Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                orderConfirmVo.setHasStock(collect);
            }

        },executor);

        // 优惠信息
        Integer integration = memberVo.getIntegration();
        orderConfirmVo.setIntegration(integration);

        // 防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        orderConfirmVo.setToken(token);
        // 把令牌放入redis，有效期30分钟
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberVo.getId(),token,30, TimeUnit.MINUTES);

        CompletableFuture.allOf(userAddrFuture, checkedItemFuture).get();

        return orderConfirmVo;
    }


    // @Transactional 本地事务，在分布式系统，只能控制住自己的回滚，控制不了其他服务的回滚
    // 分布式事务： 最大原因：网络问题+分布式机器
 //   @GlobalTransactional
    @Transactional
    @Override
    public SumitOrderResponseVo submitOrder(OrderSubimtVo orderSubimtVo) {
        orderSubimtVoThreadLocal.set(orderSubimtVo);
        SumitOrderResponseVo responseVo = new SumitOrderResponseVo();
        responseVo.setCode(0);
        String token = orderSubimtVo.getToken();
        MemberVo memberVo = OrderInterceptor.threadLocal.get();
//        String s = redisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberVo.getId());
//        if(token.equals(s)){
//            // 下单成功
//        }else {
//            //下单失败
//        }
        /**
         * 为了保证令牌的对比和删除必须保证原子性，我们使用lua脚本来验证
         * 返回0 令牌失败 返回1 删除成功
         * */
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        // new DefaultRedisScript<返回类型>(脚本,返回类型),key,需要对比的值)
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberVo.getId()), token);
        if(result == 0L){
            // 令牌验证失败
            responseVo.setCode(1);
            return responseVo;
        }else {
            // 令牌验证成功
            // 下单：去创建订单，验令牌，验价格，锁库存
            OrderCreateTo order = createOrder();
            // 进行金额对比（允许在0.01以下的误差）
            BigDecimal payPriceVo = orderSubimtVo.getPayPrice();
            BigDecimal payPrice = order.getPayPrice();
            if(Math.abs(payPriceVo.subtract(payPrice).doubleValue())<0.01){
                // 金额比对成功，生成订单，锁库存（锁库存失败会全部回滚）
                saveOrder(order);
                // 锁库存
                WareSkuLockVo lockVo = new WareSkuLockVo();
                lockVo.setOrderSn(order.getOrder().getOrderSn());
                lockVo.setLocks(order.getOrderItems().stream().map(item->{
                    OrderItemVo orderItemVo = new OrderItemVo();
                    orderItemVo.setSkuId(item.getSkuId());
                    orderItemVo.setConut(item.getSkuQuantity());
                    orderItemVo.setTitle(item.getSkuName());
                    return orderItemVo;
                }).collect(Collectors.toList()));
                // TODO 远程锁库存
                // 因为是远程调用，库存成功，但因为网络原因超时了，订单回滚，库存不回滚
                R r = wareFeignService.orderLockStock(lockVo);
                if(r.getCode()==0){
                    // 锁库存成功
                    rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder());
                    responseVo.setOrderEntity(order.getOrder());
                    return responseVo;
                }else {
                    // 锁库存失败
                    responseVo.setCode(3);
                    String msg = (String) r.get("msg");
                    throw new NostockExcption(msg);
                }

            }else {
                responseVo.setCode(2);
                return responseVo;
            }
        }
    }

    @Override
    public OrderEntity getInfoBySn(String orderSn) {
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return orderEntity;
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {
        Long id = orderEntity.getId();
        OrderEntity order = this.getById(id);
        if(order.getStatus()== OrderStatusEnum.CREATE_NEW.getCode()){
            // 已经超时还是待付款状态,把订单的状态修改成已取消
            OrderEntity entity = new OrderEntity();
            entity.setId(id);
            entity.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(entity);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(order,orderTo);
            rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderTo);
        }
    }

    @Override
    public PayVo payOrder(String orderSn) {
        OrderEntity orderEntity = this.getInfoBySn(orderSn);
        PayVo payVo = new PayVo();
        // 设置订单号
        payVo.setOut_trade_no(orderEntity.getOrderSn());
        // 总价格
        BigDecimal bigDecimal = orderEntity.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
        payVo.setTotal_amount(bigDecimal.toString());
        // 订单标题（取第一个商品的标题）
        List<OrderItemEntity> list = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
        payVo.setSubject(list.get(0).getSpuName());
        // 备注（q取第一个商品的属性）
        payVo.setBody(list.get(0).getSkuAttrsVals());
        return payVo;
    }

    @Override
    public PageUtils listWithItem(Map<String, Object> params) {
        MemberVo memberVo = OrderInterceptor.threadLocal.get();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id",memberVo.getId()).orderByDesc("id")
        );
        List<OrderEntity> records = page.getRecords();
        List<OrderEntity> collect = records.stream().map(item -> {
            String orderSn = item.getOrderSn();
            List<OrderItemEntity> list = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
            item.setOrderItems(list);
            return item;
        }).collect(Collectors.toList());
        page.setRecords(collect);
        return new PageUtils(page);
    }

    /**
     * 处理支付成功后的状态修改
     * */
    @Override
    public String handlePayResult(PayAsyncVo vo) {
        if(vo!=null){
            // 保存支付订单数据到数据库
            PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
            paymentInfoEntity.setOrderSn(vo.getOut_trade_no());
            paymentInfoEntity.setAlipayTradeNo(vo.getTrade_no());
            paymentInfoEntity.setTotalAmount(new BigDecimal(vo.getTotal_amount()));
            paymentInfoEntity.setPaymentStatus(vo.getTrade_status());
            paymentInfoEntity.setCreateTime(vo.getNotify_time());
            paymentInfoService.save(paymentInfoEntity);

            // 修改订单的状态
            String status = vo.getTrade_status();
            if(status.equals("TRADE_SUCCESS") || status.equals("TRADE_FINISHED")){
                String orderSn = vo.getOut_trade_no();
                baseMapper.updateStatusByOrderSn(orderSn,OrderStatusEnum.PAYED.getCode());
                return "success";
            }else {
                return "error";
            }
        }else {
            return "error";
        }
    }

    @Override
    public void saveSeckillOrder(SeckillOrderTo seckillOrderTo) {
        // 保存订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        BigDecimal payAmount = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal(seckillOrderTo.getNum()));
        orderEntity.setPayAmount(payAmount);
        this.save(orderEntity);

        // 保存订单商品详细信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setSkuId(seckillOrderTo.getSkuId());
        orderItemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderItemEntity.setSkuPrice(seckillOrderTo.getSeckillPrice());
        orderItemEntity.setSkuQuantity(seckillOrderTo.getNum());
        orderItemService.save(orderItemEntity);
    }

    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        this.save(orderEntity);
        orderItemService.saveBatch(order.getOrderItems());
    }

    private OrderCreateTo createOrder() {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        // 生成订单号（利用Mybatis的工具类）
        String orderSn = IdWorker.getTimeId();
        // 生成订单信息
        OrderEntity orderEntity = bulidOrder(orderSn);

        // 获取所有订单项
        List<OrderItemEntity> orderItems = getOrderItems(orderSn);

        // 计算价格和优惠信息
        computePrice(orderEntity, orderItems);

        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItems(orderItems);
        orderCreateTo.setFare(orderEntity.getFreightAmount());
        orderCreateTo.setPayPrice(orderEntity.getPayAmount());
        return orderCreateTo;
    }

    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItems) {
        // 1、定义好相关金额，然后遍历购物项进行计算
        // 总价格
        BigDecimal total = new BigDecimal("0");
        //相关优惠信息
        // 优惠卷抵扣金额
        BigDecimal coupon = new BigDecimal("0");
        // 积分优惠金额
        BigDecimal integration = new BigDecimal("0");
        // 促销优惠金额
        BigDecimal promotion = new BigDecimal("0");
        // 积分
        BigDecimal gift = new BigDecimal("0");
        // 成长值
        BigDecimal growth = new BigDecimal("0");

        // 遍历订单项将所有的优惠信息进行相加
        for (OrderItemEntity itemEntity : orderItems) {
            coupon = coupon.add(itemEntity.getCouponAmount()); // 优惠卷抵扣
            integration = integration.add(itemEntity.getIntegrationAmount()); // 积分优惠分解金额
            promotion = promotion.add(itemEntity.getPromotionAmount()); // 商品促销分解金额
            gift = gift.add(new BigDecimal(itemEntity.getGiftIntegration().toString())); // 赠送积分
            growth = growth.add(new BigDecimal(itemEntity.getGiftGrowth())); // 赠送成长值
            total = total.add(itemEntity.getRealAmount()); //优惠后的总金额
        }

        // 2、设置订单金额
        // 订单总金额
        orderEntity.setTotalAmount(total);
        // 应付总额 = 订单总额 + 运费信息
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        // 促销优化金额（促销价、满减、阶梯价）
        orderEntity.setPromotionAmount(promotion);
        // 优惠券抵扣金额
        orderEntity.setCouponAmount(coupon);

        // 3、设置积分信息
        // 订单购买后可以获得的成长值
        orderEntity.setGrowth(growth.intValue());
        // 积分抵扣金额
        orderEntity.setIntegrationAmount(integration);
        // 可以获得的积分
        orderEntity.setIntegration(gift.intValue());
        // 删除状态【0->未删除；1->已删除】
        orderEntity.setDeleteStatus(0);

    }

    // 获取所有订单项
    private List<OrderItemEntity> getOrderItems(String orderSn) {
        List<OrderItemVo> checkedItem = cartFeignService.getCheckedItem();
        List<OrderItemEntity> collect = checkedItem.stream().map(item -> {
            OrderItemEntity orderItemEntity = getOrderItem(item);
            orderItemEntity.setOrderSn(orderSn);
            return orderItemEntity;
        }).collect(Collectors.toList());
        return collect;
    }

    // 构建每一个订单项信息
    private OrderItemEntity getOrderItem(OrderItemVo item) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        // 设置spu的相关信息
        R r = productFeignService.getSpuInfoBySkuId(item.getSkuId());
        SpuInfoVo data = r.getData(new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(data.getId());
        orderItemEntity.setSpuBrand(data.getBrandId().toString());
        orderItemEntity.setSpuName(data.getSpuName());

        // 设置sku的相关信息
        orderItemEntity.setSkuId(item.getSkuId());
        orderItemEntity.setSkuPrice(item.getPrice());
        orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(item.getSkuAttr(),";"));
        orderItemEntity.setSkuPic(item.getImage());
        orderItemEntity.setSkuQuantity(item.getConut());// 商品的数量

        // 总价以及优惠信息
        // 5、设置商品积分信息
        // 赠送积分 移弃小数值
        orderItemEntity.setGiftIntegration(item.getPrice().multiply(new BigDecimal(item.getConut().toString())).intValue());
        // 赠送成长值
        orderItemEntity.setGiftGrowth(item.getPrice().multiply(new BigDecimal(item.getConut().toString())).intValue());

        // 6、订单项的价格信息
        // 这里需要计算商品的分解信息
        // 商品促销分解金额
        orderItemEntity.setPromotionAmount(new BigDecimal("0"));
        // 优惠券优惠分解金额
        orderItemEntity.setCouponAmount(new BigDecimal("0"));
        // 积分优惠分解金额
        orderItemEntity.setIntegrationAmount(new BigDecimal("0"));
        // 商品价格乘以商品购买数量=总金额(未包含优惠信息)
        BigDecimal origin = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
        // 总价格减去优惠卷-积分优惠-商品促销金额 = 总金额
        origin.subtract(orderItemEntity.getPromotionAmount())
                .subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getIntegrationAmount());
        // 该商品经过优惠后的分解金额
        orderItemEntity.setRealAmount(origin);


        //TODO 优惠信息后续完善
        return orderItemEntity;
    }

    private OrderEntity bulidOrder(String orderSn) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderSn);
        MemberVo memberVo = OrderInterceptor.threadLocal.get();
        orderEntity.setMemberId(memberVo.getId());
        // 获取运费、收货地址信息
        OrderSubimtVo orderSubimtVo = orderSubimtVoThreadLocal.get();
        R r = memberFeignService.getFare(orderSubimtVo.getAddrId());
        FareVo data = r.getData(new TypeReference<FareVo>() {
        });
        // 运费
        orderEntity.setFreightAmount(data.getFare());
        // 收货地址信息
        MemberAddrVo memberAddress = data.getMemberAddress();
        orderEntity.setReceiverPhone(memberAddress.getPhone());
        orderEntity.setReceiverName(memberAddress.getName());
        orderEntity.setReceiverProvince(memberAddress.getProvince());
        orderEntity.setReceiverCity(memberAddress.getCity());
        orderEntity.setReceiverRegion(memberAddress.getRegion());
        orderEntity.setReceiverDetailAddress(memberAddress.getDetailAddress());
        return orderEntity;
    }

}